// 1. 实现Event Bus（发布-订阅核心）
class EventBus {
  constructor() {
    // 存储事件-监听器映射：key=事件名，value=监听器数组
    this.handlers = {};
  }

  // 订阅事件
  on(eventName, callback) {
    if (!this.handlers[eventName]) this.handlers[eventName] = [];
    this.handlers[eventName].push(callback);
  }

  // 发布事件（浅拷贝避免once移除时影响遍历）
  emit(eventName, ...args) {
    const handlers = this.handlers[eventName]?.slice() || [];
    handlers.forEach(callback => callback(...args));
  }

  // 取消订阅
  off(eventName, callback) {
    const handlers = this.handlers[eventName];
    if (!handlers) return;
    const index = handlers.indexOf(callback);
    if (index !== -1) handlers.splice(index, 1);
    if (handlers.length === 0) delete this.handlers[eventName];
  }

  // 单次订阅
  once(eventName, callback) {
    const wrapper = (...args) => {
      callback(...args);
      this.off(eventName, wrapper); // 触发后自动取消
    };
    this.on(eventName, wrapper);
  }
}

// 2. 业务类：博主（发布者）、用户（订阅者）
class Blogger {
  constructor(name) {
    this.name = name;
  }

  // 发布新文章（触发事件）
  publishArticle(title, link) {
    console.log(`\n${this.name}发布新文章：《${title}》`);
    notificationBus.emit('newArticle', { bloggerName: this.name, title, link });
  }
}

class User {
  constructor(name) {
    this.name = name;
    this.followCallback = null; // 存储关注监听器，用于取消
  }

  // 关注博主（订阅事件）
  follow(blogger) {
    this.followCallback = (article) => {
      console.log(`${this.name}收到通知：${article.bloggerName}的新文章《${article.title}》，链接：${article.link}`);
    };
    notificationBus.on('newArticle', this.followCallback);
    console.log(`${this.name}已关注${blogger.name}`);
  }

  // 取消关注（移除监听器）
  unfollow() {
    if (this.followCallback) {
      notificationBus.off('newArticle', this.followCallback);
      console.log(`${this.name}已取消关注`);
    }
  }

  // 单次订阅（只看一次新文章）
  watchOnce() {
    notificationBus.once('newArticle', (article) => {
      console.log(`${this.name}（单次）收到通知：${article.bloggerName}的新文章《${article.title}》`);
    });
    console.log(`${this.name}设置只接收一次新文章通知`);
  }
}

// 3. 测试
const notificationBus = new EventBus();
const techBlogger = new Blogger('前端技术君');
const user1 = new User('小明');
const user2 = new User('小红');

user1.follow(techBlogger); // 小明关注博主
user2.watchOnce(); // 小红设置单次通知

techBlogger.publishArticle('观察者模式实战', 'https://example.com/observer'); 
// 输出：小明收到通知... 小红（单次）收到通知...

techBlogger.publishArticle('TS类型体操', 'https://example.com/ts'); 
// 输出：小明收到通知...（小红已取消）

user1.unfollow(); // 小明取消关注
techBlogger.publishArticle('Vue3源码', 'https://example.com/vue3'); 
// 输出：无通知（小明已取消）